package xml;

import java.io.IOException;
import javax.xml.parsers.*;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import edu.vcu.csl.ess.*;
import edu.vcu.csl.ess.RawModel.*;

/** 
 * PABSTE - On the Performance of Accelerated Biological Simulations
 * utilizing Triggers and Events.
 * 
 * This software library is subject to the terms and conditions
 * defined in the GNU General Public License v3.
 * 
 * Developed at:
 * School Of Electrical and Computer Engineering
 * Computer Systems Laboratory
 * Virginia Commonwealth University
 * Richmond, VA, USA
 * 
 * @author Brad R. Geltz et al.
 * @version 4.0
 *  
 */

public class ESSXMLParser
{
	
	private String                  XMLFileName;

	private DocumentBuilderFactory  factory;
	private DocumentBuilder         builder;
	private Document                document;
	
	private RawModel				model;

	public ESSXMLParser(String f, RawModel model){
		
		if(!f.equals("")){
			XMLFileName = f;
		}
		else{
			System.err.println("ERROR: XML file not set!");
		}
		
		this.model = model;
	}

	public void parseData() throws ParserConfigurationException, SAXException, IOException{
		
		factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		factory.setValidating(true);
		factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
		"http://www.w3.org/2001/XMLSchema");

		builder = factory.newDocumentBuilder();
		builder.setErrorHandler( new SimpleErrorHandler() );

		document = builder.parse(XMLFileName);
		
		getConstants();
		getSpecies();
		getReactions();
		getEvents();
		getPeriodics();
		getTriggers();
		getSimulation();	
		
	}


	/*
	 *  Grab Simulation Data
	 */

	public void getSimulation(){
		
//		System.out.println("--- SIMULATION ---");

		NodeList simulationGroup = document.getElementsByTagName("simulation");
		Node simulation = simulationGroup.item(0);
		NodeList simulationParams = simulation.getChildNodes();
				
		for(int i=0; i<simulationParams.getLength(); i++){
			
			Node currentParam = simulationParams.item(i);			
			
			if (currentParam.getNodeType() != Node.TEXT_NODE){				
				
//				System.out.println(currentParam.getNodeName() + " = "
//						+ currentParam.getTextContent() );
				
				if(currentParam.getNodeName().equals("start")){					
					model.setStartTime(Double.parseDouble(currentParam.getTextContent()));					
				}
				else if(currentParam.getNodeName().equals("printInterval")){					
					model.setPrintInterval(Double.parseDouble(currentParam.getTextContent()));								
				}
				else if(currentParam.getNodeName().equals("end")){					
					model.setEndTime(Double.parseDouble(currentParam.getTextContent()));								
				}
				else if(currentParam.getNodeName().equals("seed")){					
					model.setSeed(Integer.parseInt(currentParam.getTextContent()));								
				}
				else if(currentParam.getNodeName().equals("outputFilename")){					
					model.setOutputFilename(currentParam.getTextContent());								
				}
				
			}
		}
	}


	/*
	 *  Grab Constant Data
	 */

	public void getConstants(){
//		System.out.println("--- CONSTANTS ---");

		NodeList constantParent = document.getElementsByTagName("constant");

		for(int i=0; i<constantParent.getLength(); i++){
			
			Node constant = constantParent.item(i);
			NodeList constantParams = constant.getChildNodes();
			
			Constant c = new Constant();

			for(int j=0; j<constantParams.getLength(); j++){
				
				Node currentParam = constantParams.item(j);

				if (currentParam.getNodeType() != Node.TEXT_NODE){					
					
//					System.out.println(currentParam.getNodeName() + " = "
//							+ currentParam.getTextContent() );
					
					if(currentParam.getNodeName().equals("name")){						
						c.name = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("value")){						
						c.value = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("comment")){						
						c.comment = currentParam.getTextContent();											
					}
					
				}
			}
			
			model.addConstant(c.name, c.value, c.comment);			
			
		}
	}


	/*
	 *  Grab Species Data
	 */

	public void getSpecies(){
//		System.out.println("--- SPECIES ---");

		NodeList speciesParent = document.getElementsByTagName("species");

		for(int i=0; i<speciesParent.getLength(); i++){
			
			Node species = speciesParent.item(i);
			NodeList speciesParams = species.getChildNodes();
			
			Species s = new Species();

			for(int j=0; j<speciesParams.getLength(); j++){
				
				Node currentParam = speciesParams.item(j);

				if (currentParam.getNodeType() != Node.TEXT_NODE){
					
//					System.out.println(currentParam.getNodeName() + " = "
//							+ currentParam.getTextContent() );
					
					if(currentParam.getNodeName().equals("name")){						
						s.name = currentParam.getTextContent();											
					}					
					else if(currentParam.getNodeName().equals("population")){						
						s.population = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("population")){						
						s.comment = currentParam.getTextContent();											
					}					
				}
			}
			
			model.addSpecies(s.name, s.population, s.comment);		
			
		}
	}


	/*
	 *  Grab Reaction Data
	 */

	public void getReactions(){
//		System.out.println("--- REACTIONS ---");

		NodeList reactionParent = document.getElementsByTagName("reaction");

		for(int i=0; i<reactionParent.getLength(); i++){
			
			Node reaction = reactionParent.item(i);
			NodeList reactionParams = reaction.getChildNodes();
			Reaction r = new Reaction();

			for(int j=0; j<reactionParams.getLength(); j++){
				
				Node currentParam = reactionParams.item(j);

				if (currentParam.getNodeType() != Node.TEXT_NODE){
					
//					System.out.println(currentParam.getNodeName() + " = "
//							+ currentParam.getTextContent() );
					
					if(currentParam.getNodeName().equals("name")){						
						r.name = currentParam.getTextContent();											
					}					
					else if(currentParam.getNodeName().equals("equation")){						
						r.equation = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("rate")){						
						r.rate = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("comment")){						
						r.comment = currentParam.getTextContent();											
					}					
				}
			}
			
			model.addReaction(r.name, r.equation, r.rate, r.comment);
			
		}
	}


	/*
	 *  Grab Events Data
	 */

	public void getEvents(){
//		System.out.println("\n--- EVENTS ---\n");

		NodeList eventParent = document.getElementsByTagName("event");

		for(int i=0; i<eventParent.getLength(); i++){
			
			Node event = eventParent.item(i);
			NodeList eventParams = event.getChildNodes();
			Event e = new Event();

			for(int j=0; j<eventParams.getLength(); j++){
				
				Node currentParam = eventParams.item(j);

				if (currentParam.getNodeType() != Node.TEXT_NODE){
										
//					System.out.println(currentParam.getNodeName() + " = "
//							+ currentParam.getTextContent() );
					
					if(currentParam.getNodeName().equals("name")){						
						e.name = currentParam.getTextContent();											
					}					
					else if(currentParam.getNodeName().equals("time")){						
						e.time = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("action")){						
						e.action = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("comment")){						
						e.comment = currentParam.getTextContent();											
					}
					
				}
			}
			
			model.addEvent(e.name, e.time, e.action, e.comment);
			
		}
	}


	/*
	 *  Grab Periodics Data
	 */

	public void getPeriodics(){
//		System.out.println("--- PERIODICS ---");

		NodeList periodicParent = document.getElementsByTagName("periodic");

		for(int i=0; i<periodicParent.getLength(); i++){
			
			Node periodic = periodicParent.item(i);
			NodeList periodicParams = periodic.getChildNodes();
			Periodic p = new Periodic();

			for(int j=0; j<periodicParams.getLength(); j++){
				Node currentParam = periodicParams.item(j);

				if (currentParam.getNodeType() != Node.TEXT_NODE){
					
//					System.out.println(currentParam.getNodeName() + " = "
//							+ currentParam.getTextContent() );
					
					if(currentParam.getNodeName().equals("name")){						
						p.name = currentParam.getTextContent();											
					}					
					else if(currentParam.getNodeName().equals("start")){						
						p.start = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("period")){						
						p.period = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("end")){						
						p.end = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("action")){						
						p.action = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("comment")){						
						p.comment = currentParam.getTextContent();											
					}
					
				}
			}
	
			model.addPeriodic(p.name, p.start, p.period, p.end, p.action, p.comment);	
			
		}
	}


	/*
	 * Get Triggers Data
	 */

	public void getTriggers(){
//		System.out.println("--- TRIGGERS ---");

		NodeList triggerParent = document.getElementsByTagName("trigger");

		for(int i=0; i<triggerParent.getLength(); i++){
			Node trigger = triggerParent.item(i);
			NodeList triggerParams = trigger.getChildNodes();
			Trigger t = new Trigger();

			for(int j=0; j<triggerParams.getLength(); j++){
				
				Node currentParam = triggerParams.item(j);

				if (currentParam.getNodeType() != Node.TEXT_NODE){
					 
//					System.out.println(currentParam.getNodeName() + " = "
//							+ currentParam.getTextContent() );
					
					if(currentParam.getNodeName().equals("name")){						
						t.name = currentParam.getTextContent();											
					}					
					else if(currentParam.getNodeName().equals("condition")){						
						t.condition = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("action")){						
						t.action = currentParam.getTextContent();											
					}
					else if(currentParam.getNodeName().equals("comment")){						
						t.comment = currentParam.getTextContent();											
					}
					
				}
			}
			
			model.addTrigger(t.name, t.condition, t.action, t.comment);
			
		}
	}
	
	public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException{
		
		if (args.length != 2) {
			System.out.println("usage: java ESSXMLParser <filename> <model_type>");
			System.exit(1);
		}
		
		String XMLFileName = args[0];
		RawModel model = new RawModel();
		ESSXMLParser parser = new ESSXMLParser(XMLFileName,model);
		parser.parseData();
		System.out.println("Parsing complete.");
		
		if (model.run() == false) {
			model.printErrors();	    
		} else {
			System.out.println("Construction complete...");
			//			ModelCodeGen.generateOutputFile(model);			
			//			ModelCodeGen.generateDirectMethodOutputFile(model);

			if(Integer.parseInt(args[1]) == 1){
				System.out.println("Using EEHDM...");
				ModelCodeGenC.generateOutputFile(model);
			}
			else if(Integer.parseInt(args[1]) == 2){
				System.out.println("Using Naive EHDM...");
				ModelCodeGenC.generateNaiveOutputFile(model);
			}
			else if(Integer.parseInt(args[1]) == 99){
				System.out.println("Using Java EEHDM...");
//				ModelCodeGenC.generateOutputFile(model);
				ModelCodeGen.generateOutputFile(model);
			}
			else{
				System.out.println("Using Naive DM");				
				ModelCodeGenC.generateDirectMethodOutputFile(model);
			}
		}		
	}	
}
